package com.rlax.framework.plugin.shiro;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.List;
import java.util.Map;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;

import com.rlax.framework.common.CacheKey;
import com.rlax.framework.common.Consts;
import com.rlax.framework.support.cache.CacheExtKit;
import com.rlax.web.model.Res4s;
import com.rlax.web.model.Role;
import com.rlax.web.model.Role4s;
import com.rlax.web.model.User;
import com.rlax.web.model.User4s;

/**
 * Shiro 工具类
 * gt.registerFunctionPackage("so",new ShiroExt ());
 * 在模版中通过 so.isGuest() 
 * @author Rlax
 *
 */
public class ShiroUtils {
    /**
     * The guest tag
     *
     * @return
     */
    public static boolean isGuest() {
        return getSubject() == null || getSubject().getPrincipal() == null;
    }

    /**
     * The user tag
     *
     * @return
     */
    public static boolean isUser() {
        return getSubject() != null && getSubject().getPrincipal() != null;
    }

    /**
     * The authenticated tag
     *
     * @return
     */
    public static boolean isAuthenticated() {
        return getSubject() != null && getSubject().isAuthenticated();
    }

    public static boolean isNotAuthenticated() {
        return !isAuthenticated();
    }

    /**
     * The principal tag
     *
     * @param map
     * @return
     */
    public static String principal(Map<?, ?> map) {
        String strValue = null;
        if (getSubject() != null) {

            // Get the principal to print out
            Object principal;
            String type = map != null ? (String) map.get("type") : null;
            if (type == null) {
                principal = getSubject().getPrincipal();
            } else {
                principal = getPrincipalFromClassName(type);
            }
            String property = map != null ? (String) map.get("property") : null;
            // Get the string value of the principal
            if (principal != null) {
                if (property == null) {
                    strValue = principal.toString();
                } else {
                    strValue = getPrincipalProperty(principal, property);
                }
            }

        }

        if (strValue != null) {
            return strValue;
        } else {
            return null;
        }
    }

    /**
     * The hasRole tag
     *
     * @param roleName
     * @return
     */
    public static boolean hasRole(String roleName) {
        return getSubject() != null && getSubject().hasRole(roleName);
    }

    /**
     * The lacksRole tag
     *
     * @param roleName
     * @return
     */
    public static boolean lacksRole(String roleName) {
        boolean hasRole = getSubject() != null
                && getSubject().hasRole(roleName);
        return !hasRole;
    }

    /**
     * The hasAnyRole tag
     *
     * @param roleNames
     * @return
     */
    public static boolean hasAnyRole(String roleNames) {
        boolean hasAnyRole = false;

        Subject subject = getSubject();

        if (subject != null) {

            // Iterate through roles and check to see if the user has one of the
            // roles
            for (String role : roleNames.split(",")) {

                if (subject.hasRole(role.trim())) {
                    hasAnyRole = true;
                    break;
                }

            }

        }

        return hasAnyRole;
    }

    /**
     * The hasPermission tag
     *
     * @param p
     * @return
     */
    public static boolean hasPermission(String p) {
        return getSubject() != null && getSubject().isPermitted(p);
    }

    /**
     * The lacksPermission tag
     *
     * @param p
     * @return
     */
    public static boolean lacksPermission(String p) {
        return !hasPermission(p);
    }


    private static Object getPrincipalFromClassName(String type) {
        Object principal = null;

        try {
            Class<?> cls = Class.forName(type);
            principal = getSubject().getPrincipals().oneByType(cls);
        } catch (ClassNotFoundException e) {

        }
        return principal;
    }

    private static String getPrincipalProperty(Object principal, String property) {
        String strValue = null;

        try {
            BeanInfo bi = Introspector.getBeanInfo(principal.getClass());

            // Loop through the properties to get the string value of the
            // specified property
            boolean foundProperty = false;
            for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
                if (pd.getName().equals(property)) {
                    Object value = pd.getReadMethod().invoke(principal,
                            (Object[]) null);
                    strValue = String.valueOf(value);
                    foundProperty = true;
                    break;
                }
            }

            if (!foundProperty) {
                final String message = "Property [" + property
                        + "] not found in principal of type ["
                        + principal.getClass().getName() + "]";

                throw new RuntimeException(message);
            }

        } catch (Exception e) {
            final String message = "Error reading property [" + property
                    + "] from principal of type ["
                    + principal.getClass().getName() + "]";

            throw new RuntimeException(message, e);
        }

        return strValue;
    }

    protected static Subject getSubject() {
        return SecurityUtils.getSubject();
    }

	/**
	 * 获取平台登录用户
	 * @return
	 */
	public static User getLoginUser() {
		User user = null;
		if (isAuthenticated()) {
			user = (User) getSubject().getSession().getAttribute(Consts.SESSION_USER);
		}
		return user;
	}
	
	/**
	 * 获取系统登录用户
	 * @return
	 */
	public static Role getLoginUserRole() {
		Role role = null;
		if (isAuthenticated()) {
			role = (Role) getSubject().getSession().getAttribute(Consts.SESSION_USER_ROLE);
		}
		return role;
	}
    
	/**
	 * 获取系统登录用户
	 * @return
	 */
	public static User4s getLoginUser4s() {
		User4s user = null;
		if (isAuthenticated()) {
			user = (User4s) getSubject().getSession().getAttribute(Consts.SESSION_USER);
		}
		return user;
	}
	
	/**
	 * 获取系统登录用户
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<Role4s> getLoginUserRole4s() {
		List<Role4s> role = null;
		if (isAuthenticated()) {
			role = (List<Role4s>) getSubject().getSession().getAttribute(Consts.SESSION_USER_ROLE);
		}
		return role;
	}
	
	/**
	 * 获取用户编码对应 session，用户信息发生变更时刷新
	 * @return
	 */
	public static void refreshSessionByUserId(Long id) {
		if (isAuthenticated()) {
			User4s user = User4s.dao.findById(id);
			List<Long> roleIds = User4s.dao.findRoleIdByUserID(id);
			List<Role4s> roleList = Role4s.dao.findByIds(roleIds.toArray(new Long[roleIds.size()]));
			getSubject().getSession(true).setAttribute(Consts.SESSION_USER, user);
			getSubject().getSession(true).setAttribute(Consts.SESSION_USER_ROLE, roleList);
		}
	}
	
	/**
	 * 根据角色编码，刷新角色对应菜单缓存
	 * @return
	 */
	public static void refreshMenuByRoleId(Long id) {
		CacheExtKit.remove(CacheKey.CACHE_MENU, "ROLE_" + id);
	}
	
	/**
	 * 清空所有菜单缓存
	 * @param id
	 */
	public static void clearAllMenuCache() {
		CacheExtKit.removeAll(CacheKey.CACHE_MENU);
	}
	
	/**
	 * 更新资源Url信息，如果出现资源新增修改情况更新拦截器缓存
	 */
	public static void updateResUrls()
	{
		ShiroInterceptor4s.updateUrls();
	}
	
	/**
	 * 获取资源Url信息
	 * @return
	 */
	public static List<String> getResUrls() {
		return ShiroInterceptor4s.getUrls();
	}
	
	/**
	 * 获取资源List信息
	 * @return
	 */
	public static List<Res4s> getResList() {
		return ShiroInterceptor4s.getResList();
	}
	
	/**
	 * 校验用户登录密码
	 * @param checkUser 需要校验的用户
	 * @param currentUser 当前用户
	 * @return
	 */
	public static boolean checkPwd(User4s checkUser, User4s currentUser) {
		SimpleHash hash = new SimpleHash("md5", checkUser.getPwd(), currentUser.getPhone() + currentUser.getSalt2(), 2);
		return hash.toHex().equals(currentUser.getPwd());
	}
}
